package net.client;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import net.data.AskNamesRequest;
import net.data.FindNameRequest;
import net.data.ResponseResult;
import net.data.SendMessageRequest;
import net.data.SendNameRequest;
import core.ChatNode;
import core.FindTask;
import core.RequestBroadcaster;
import core.ResponseHandler;
import db.Mapper;

public class RequestDispacher extends Thread
{
	private ChatNode node;
	
	private ExecutorService taskExecutor;
	private ExecutorCompletionService<ResponseResult> ecs;
	private List<Future<ResponseResult>> resultList;
	private List<RequestDispatcherTask> taskList;
	
	public RequestDispacher(ChatNode node)
	{
		this.node = node;
		
		taskExecutor = Executors.newCachedThreadPool();
		ecs = new ExecutorCompletionService<ResponseResult>(taskExecutor);
		taskList = new ArrayList<RequestDispatcherTask>();
		resultList = new ArrayList<Future<ResponseResult>>();
	}
	
	public void dispach(FindNameRequest request)
	{
		if(request != null)
		{
			taskList.add(new RequestDispatcherTask(request));
			this.start();
		}
	}
	
	public void dispach(FindNameRequest[] requestArray)
	{
		if(requestArray != null)
		{
			for(FindNameRequest request : requestArray)
			{
				taskList.add(new RequestDispatcherTask(request));
			}
			
			this.start();
		}
	}
	
	public void dispach(SendNameRequest request)
	{
		if(request != null)
		{
			taskList.add(new RequestDispatcherTask(request));
			this.start();
		}
	}
	
	public void dispach(SendNameRequest[] requestArray)
	{
		if(requestArray != null)
		{
			for(SendNameRequest request : requestArray)
			{
				taskList.add(new RequestDispatcherTask(request));
			}
			this.start();
		}
	}
	
	public void dispach(AskNamesRequest request)
	{
		if(request != null)
		{
			taskList.add(new RequestDispatcherTask(request));
			this.start();
		}
	}
	
	public void dispach(AskNamesRequest[] requestArray)
	{
		if(requestArray != null)
		{
			for(AskNamesRequest request : requestArray)
			{
				taskList.add(new RequestDispatcherTask(request));
			}
		}
		this.start();
	}
	
	public void dispach(SendMessageRequest request)
	{
		if(request != null)
		{
			taskList.add(new RequestDispatcherTask(request));
			this.start();
		}
	}
	
	@Override
	public void run()
	{
		if(taskList != null)
		{
			for(RequestDispatcherTask task : taskList)
			{
				resultList.add(ecs.submit(task));
			}
			
			taskExecutor.shutdown();
			
			ResponseHandler responseHandler = new ResponseHandler(node);
			
			int size = resultList.size();
			ResponseResult[] results = new ResponseResult[size];
			for(int n = 0; n != resultList.size(); ++n)
			{
				try
				{
					ResponseResult result = ecs.take().get();
					results[n] = result;
					if(result == null)
					{
						String query = taskList.get(0).query;
						String queryType = query.substring(0,query.indexOf("?"));
						node.getMainWindow().appendText(queryType  + " timeout");
					}
					else
					{
						node.getMainWindow().appendText(result.getQuery() + " > " + result.getToAddress());
					}
					
					responseHandler.handle(result);
				}
				catch(InterruptedException ex){/* just ignore the failed task */}
				catch(ExecutionException ex){/* ignore */}
			}
			
			//all the tasks are done
			
			
			FindTask task = node.getFindTask();
			if(task != null && results[0].getQueryType().equals("asknames"))
			{
				String name = task.getFindName();
				String myAddress = node.getIp() + ":" + node.getPort();
				
				Mapper userbase = node.getUserBase();
				if(!userbase.containsName(name))
				{
					task.execute();
					RequestBroadcaster broadcaster = node.getRequestBroadcaster();
					broadcaster.broadcastFindName(name, myAddress, 1);
				}
				else
				{
					node.getMainWindow().appendText("Peer \"" + name + "\" found: "+ userbase.getAddress(name));
					node.endFindTask();
				}
			}
			
			
		}
	}
}
